package me.zhengjie.minbearwcs.serivce.mission.generate.inbound;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.stock.inf.PalletDao;
import me.zhengjie.minbearwcs.entity.DTO.inbound.wcs.InboundMissionFactor;
import me.zhengjie.minbearwcs.serivce.mission.locker.StorageLockCollector;
import me.zhengjie.minbearwcs.serivce.mission.storageProcessor.locker.Floor5Or6.InboundStorage5Or6FloorLocker;
import me.zhengjie.minbearwcs.utils.JsonUtil;
import me.zhengjie.minbearwcs.utils.LogFileKey;
import me.zhengjie.observer.caster.WcsMulticaster;
import me.zhengjie.observer.event.DefaultEvent;
import me.zhengjie.observer.type.inbound.InboundEventType;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionTemplate;
import me.zhengjie.minbearwcs.dao.inbound.inf.InboundOrderDetailDao;
import me.zhengjie.minbearwcs.dao.mission.inf.WcsMissionDao;
import me.zhengjie.minbearwcs.dao.stock.inf.MaterialStockDao;
import me.zhengjie.minbearwcs.dao.stock.inf.MaterialTypeDao;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.status.InboundOrderDetailStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.WcsMission;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.status.InboundMissionFor3Or4FloorStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.status.InboundMissionFor5Or6FloorStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.type.MissionType;
import me.zhengjie.minbearwcs.entity.PO.wcs.stock.MaterialType;
import me.zhengjie.minbearwcs.serivce.mission.generate.MissionGenerator;
import me.zhengjie.minbearwcs.serivce.mission.generate.type.MissionGenType;
import me.zhengjie.minbearwcs.utils.ContentPool;
import me.zhengjie.minbearwcs.utils.IdWorker;

import java.time.LocalDateTime;

@Slf4j
@Component
@RequiredArgsConstructor
public class InboundMissionGennerator implements MissionGenerator<String> {

    private final IdWorker idWorker;
    private final MaterialStockDao materialStockDao;
    private final MaterialTypeDao materialTypeDao;
    private final InboundOrderDetailDao inboundOrderDetailDao;
    private final WcsMulticaster wcsMulticaster;
    private final InboundStorage5Or6FloorLocker inboundStorage5Or6FloorLocker;
    private final WcsMissionDao wcsMissionDao;
    private final TransactionTemplate transactionTemplate;
    private final PalletDao palletDao;


    private final Object lock = new Object();


    @Override
    public MissionGenType type() {
        return MissionGenType.INBOUND;
    }

    @Override
    public Tuple<String> generate(Object source) {
        MDC.put(LogFileKey.KEY, LogFileKey.Module.INBOUND);
        InboundMissionFactor missionSource = (InboundMissionFactor) source;
        log.info("接收到入库申请 ： {}", JsonUtil.getJsonInfo(missionSource));

        // 更新工单状态
        wcsMulticaster.multicastEvent(DefaultEvent.create(source), InboundEventType.INBOUND_CALL_AGV);

        // 更新明细状态
        Tuple<String> res = parsingStatusChange(missionSource);
        if (!res.success) {
            return res;
        }

        Tuple<String> tuple = null;
        synchronized (StorageLockCollector.lockStorage5FOr6F) {
            tuple = transactionTemplate.execute(status -> {
                try {
                    //更新物料批次号
                    String batchCode = missionSource.getInboundOrderCode() + "_" +
                            LocalDateTime.now().format(ContentPool.DATE_FORMAT_TO_DAY);


                    String palletCode = missionSource.getPalletCode();
                    palletDao.updateBatchCodeAndInboundDate(
                            palletCode, batchCode, LocalDateTime.now()
                    );
                    materialStockDao.updateBatchCodeAndInboundDateByPallet(
                            palletCode,
                            batchCode,
                            LocalDateTime.now()
                    );

                    String materialCode = palletDao.selectMaterialCode(palletCode);

                    // 判断物料信息有无维护,没有则生成3/4F任务
                    MaterialType materialType = materialTypeDao.selectByMaterialCode(materialCode);
                    if (materialType == null) {
                        log.info("栈板 {} ， 物料 {} ，没有找到对应的物料信息，物料将搬运到3或4楼储位",
                                missionSource.getPalletCode(), materialCode);
                        storage3Or4FMissionCreate(missionSource);
                        return Tuple.tuple("没有找到对应的物料信息，物料将搬运到3或4楼储位", true);
                    }


                    // 5/6F同一批次有无空位
                    // 5/6F同一工单大储位有无空位
                    //  放到新的大储位
                    if (storage5Or6FMissionCreate(missionSource, materialCode, batchCode)) {
                        return Tuple.tuple(null, true);
                    }

                    // 爆仓放3/4F
                    storage3Or4FMissionCreate(missionSource);
                    log.info("栈板 {} ， 物料 {} ，5或6楼没有空闲储位，物料将搬运到3或4楼储位",
                            missionSource.getPalletCode(), materialCode);
                    return Tuple.tuple("5或6楼没有空闲储位，物料将搬运到3或4楼储位", true);


                } catch (Exception e) {
                    log.error("栈板：{}，所属入库工单任务生成出错", missionSource.getPalletCode(), e);
                    status.setRollbackOnly();
                    return Tuple.tuple("入库工单任务生成出错", false);
                }
            });
        }


        assert tuple != null;
        if (!tuple.success) {
            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                    InboundOrderDetailStatus.WAIT_FOR_CALL_AGV);
        }

        MDC.remove(LogFileKey.KEY);
        return tuple;

    }

    public Tuple<String> parsingStatusChange(InboundMissionFactor missionSource) {
        if (InboundOrderDetailStatus.WAIT_FOR_CALL_AGV == inboundOrderDetailDao.getStatusById(missionSource.getAssociatedDetailId())) {
            synchronized (lock) {
                if (InboundOrderDetailStatus.WAIT_FOR_CALL_AGV == inboundOrderDetailDao.getStatusById(missionSource.getAssociatedDetailId())) {
                    return transactionTemplate.execute(status -> {
                        try {
                            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                                    InboundOrderDetailStatus.PARSING);
                            return Tuple.tuple(null, true);
                        } catch (Exception e) {
                            log.error("出库工单明细状态转换出错", e);
                            status.setRollbackOnly();
                            return Tuple.tuple("出库工单明细状态转换出错", false);
                        }

                    });
                }
            }
        }

        return Tuple.tuple("叫车任务已生成", false);
    }


    public void storage3Or4FMissionCreate(InboundMissionFactor missionSource) {
        String missionId = "Task_Inbound_3/4F_" + idWorker.nextId();
        WcsMission wcs3Or4FMission = new WcsMission(null, missionId, missionSource.getInboundOrderCode(), MissionType.INBOUND_3_OR_4_FLOOR.name(),
                InboundMissionFor3Or4FloorStatus.WAIT_FOR_STORAGE.name(), LocalDateTime.now(), null, null,
                1, missionSource.getAssociatedDetailId().toString());
        wcsMissionDao.insert(wcs3Or4FMission);
        inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                InboundOrderDetailStatus.PROCESSING);
    }

    public boolean storage5Or6FMissionCreate(InboundMissionFactor missionSource, String materialCode, String batchCode) {
        Tuple<String> storage5Or6F = inboundStorage5Or6FloorLocker.lockStorage(materialCode, missionSource.getInboundOrderCode(), batchCode, missionSource.getPalletCode());
        if (storage5Or6F.success) {
            Integer associatedDetailId = missionSource.getAssociatedDetailId();
            String inboundStorage = storage5Or6F.value;
            inboundOrderDetailDao.updateStoragePointById(associatedDetailId, inboundStorage);
            String missionId = "Task_Inbound_5/6F_" + idWorker.nextId();
            WcsMission mission = new WcsMission(null, missionId, missionSource.getInboundOrderCode(), MissionType.INBOUND_5_OR_6_FLOOR.name(),
                    InboundMissionFor5Or6FloorStatus.WAIT_FOR_EXCHANGE_STORAGE.name(), LocalDateTime.now(), null, null,
                    1, missionSource.getAssociatedDetailId().toString());
            wcsMissionDao.insert(mission);
            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                    InboundOrderDetailStatus.PROCESSING);
            log.info("栈板 {} ， 入库工单{} ，生成  wcs任务 {} 成功",
                    missionSource.getPalletCode(), missionSource.getInboundOrderCode(), missionId);
            return true;
        } else {
            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                    InboundOrderDetailStatus.WAIT_FOR_CALL_AGV);
            return false;
        }
    }


}
